/*
 * Copyright (C) 2012 praetorian droid
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cstdlib>
#include <ctime>
#include <cmath>

#include "common.h"

static mtime_t mtime() {
    timeval tv;
    gettimeofday(&tv, NULL);
    return (mtime_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

void Brick::setup(const GLScreen &screen) {
    float k = (float) screen.getHeight() / screen.getWidth();
    float w = .2f;
    float h = w / 2.5f;
    srand(time(NULL));
    float x = ((float) rand() / (RAND_MAX / 2) - 1) * .85f - w / 2;
    float y = (float) rand() / RAND_MAX * screen.getRatio() * (1.f - 3 * h) + .5f * h;
    mVertices[ 0] = x;
    mVertices[ 1] = y;
    mVertices[ 2] = x;
    mVertices[ 3] = y + h;
    mVertices[ 4] = x + w;
    mVertices[ 5] = y + h;
    mVertices[ 6] = x;
    mVertices[ 7] = y;
    mVertices[ 8] = x + w;
    mVertices[ 9] = y + h;
    mVertices[10] = x + w;
    mVertices[11] = y;
}

void Brick::draw() {
    glColor4f(.4f, 8.f, .4f, 1);
    glVertexPointer(2, GL_FLOAT, 0, mVertices);
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

void Ball::setup(const GLScreen &screen) {
    maxY = screen.getRatio();
    // Reset position, momentum and acceleration
    x = y = 0;
    mMomentum.length = mMomentum.direction = 0;
    mAcceleration.direction = M_PI; // Fall down - need for emulator, that doesn't has an accelerometer
    mAcceleration.length = .03f;    // Just some experimentally determined value
}

void Ball::run() {
    mRunning = true;
    mPrevT = mtime();
}

void Ball::stop() {
    mRunning = false;
}

void Ball::onTiltChanged(float newTilt) {
    mAcceleration.direction = newTilt;
}

void Ball::move(float dt) {
    float vAcceleration = cos(mAcceleration.direction) * mAcceleration.length;
    float hAcceleration = sin(mAcceleration.direction) * mAcceleration.length;

    float vMomentum = cos(mMomentum.direction) * mMomentum.length;
    float hMomentum = sin(mMomentum.direction) * mMomentum.length;

    vMomentum += vAcceleration;
    hMomentum += hAcceleration;
    mMomentum.length = sqrtf(vMomentum * vMomentum + hMomentum * hMomentum);
    mMomentum.direction = atan2(hMomentum, vMomentum); // We use Y axis as an angle of 0!

    float dx = hMomentum * dt;
    float dy = vMomentum * dt;

    if (x + dx <= -1 || x + dx >= 1) { // side wall
        if (mCollisionHandler) mCollisionHandler(CT_WALL_TOUCH);
        mMomentum.direction = -mMomentum.direction;
        mMomentum.length *= .7f;
        dy = -dx * .8f;
        dx = -dx;
    }
    x += dx;

    if (y + dy <= -maxY || y + dy >= maxY) { // ceiling/floor
        mMomentum.direction = M_PI - mMomentum.direction;
        if (mMomentum.direction > M_PI) mMomentum.direction -= M_PI * 2;
        else if (mMomentum.direction < -M_PI) mMomentum.direction += M_PI * 2;
        if (y > 0) { // ceiling
            if (mCollisionHandler) mCollisionHandler(CT_WALL_TOUCH);
            mMomentum.length *= .5f;
            dy *= .8f;
        } else { // floor
            if (fabsf(x) < PLATFORM_WIDTH / 2) { // platform
                if (mCollisionHandler) mCollisionHandler(CT_PLATFORM_TOUCH);
                mMomentum.length *= 1.1f;
            } else { // Game over!
                if (mCollisionHandler) mCollisionHandler(CT_FLOOR_COLLISION);
                mRunning = false;
            }
        }
        dy = -dy;
    }
    y += dy;
}

void Ball::draw() {
    mtime_t t = mtime();
    float dt = .001f * (t - mPrevT);
    mPrevT = t;
    if (mRunning) move(dt);

    GLfloat ball[] = {x, y};

    glColor4f(.7f, 1.f, 1.f, 1);
    glVertexPointer(2, GL_FLOAT, 0, ball);
    glDrawArrays(GL_POINTS, 0, 1);

    // Are we happy?
    if (mRunning && gotTheBrick()) {
        if (mCollisionHandler) mCollisionHandler(CT_BRICK_COLLISION);
        mRunning = false;
    }
}

bool Ball::gotTheBrick() {
    float x1 = mBrick.getX1() - BALL_RADIUS;
    float y1 = mBrick.getY1() - BALL_RADIUS;
    float x2 = mBrick.getX2() + BALL_RADIUS;
    float y2 = mBrick.getY2() + BALL_RADIUS;
    return x > x1 && x < x2 && y > y1 && y < y2;
}

void Wall::setup(const GLScreen &screen) {
    float ratio = screen.getRatio();
    mVertices[0] = -1.f;
    mVertices[1] = -ratio;
    mVertices[2] = -1.f;
    mVertices[3] =  ratio;
    mVertices[4] =  1.f;
    mVertices[5] =  ratio;
    mVertices[6] =  1.f;
    mVertices[7] = -ratio;
}

void Wall::draw() {
    glColor4f(.7f, .8f, .7f, 1);
    glVertexPointer(2, GL_FLOAT, 0, mVertices);
    glDrawArrays(GL_LINE_STRIP, 0, 4);
}

void Platform::setup(const GLScreen &screen) {
    mVertices[0] = -PLATFORM_WIDTH / 2;
    mVertices[1] = -screen.getRatio();
    mVertices[2] =  PLATFORM_WIDTH / 2;
    mVertices[3] = -screen.getRatio();
}

void Platform::draw() {
    glColor4f(8.f, .15f, .1f, 1);
    glVertexPointer(2, GL_FLOAT, 0, mVertices);
    glDrawArrays(GL_LINES, 0, 2);
}
